package com.jackstraw.jack_base.util;


import com.alibaba.fastjson.JSON;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator;
import com.jackstraw.jack_base.util.exception.BadException;
import com.jackstraw.jack_base.util.exception.enums.ExceptionEnum;
import lombok.extern.slf4j.Slf4j;

import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.List;
import java.util.Map;

/**
 * json工具类
 * <p>
 * 注解@JsonIgnore 定义属性不转换为json
 * 注解@JsonInclude(value = Include.NON_NULL)   为null 不转换
 */
@Slf4j
public class JsonUtil {

    public static ObjectMapper objectMapper = new ObjectMapper();

    static {
        // 对象字段全部列入
        objectMapper.setSerializationInclusion(JsonInclude.Include.NON_DEFAULT);

        //反序列化的时候如果多了其他属性,不抛出异常
        objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);

        //如果是空对象的时候,不抛异常
        objectMapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);

        //属性为null的转换
        objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);

        //取消时间的转化格式,默认是时间戳,可以取消,同时需要设置要表现的时间格式
        objectMapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);

        // 统一日期格式yyyy-MM-dd HH:mm:ss
        objectMapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));

        objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);

        objectMapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance,
                ObjectMapper.DefaultTyping.NON_FINAL,
                JsonTypeInfo.As.PROPERTY);

    }

    /**
     * object转Json字符串
     *
     * @param obj
     * @param <T>
     * @return
     */
    public static <T> String obj2String(T obj) {
        if (obj == null) {
            return null;
        }
        try {
            return obj instanceof String ? (String) obj : objectMapper.writeValueAsString(obj);
        } catch (Exception e) {
            log.error("Parse object to String error", e);
            return null;
        }
    }

    /**
     * Object转json字符串并格式化美化
     *
     * @param obj
     * @param <T>
     * @return
     */
    public static <T> String obj2StringPretty(T obj) {
        if (obj == null) {
            return null;
        }
        try {
            return obj instanceof String ? (String) obj : objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(obj);
        } catch (Exception e) {
            log.error("Parse object to String error", e);
            return null;
        }
    }

    /**
     * string转object
     *
     * @param str   json字符串
     * @param clazz 被转对象class
     * @param <T>
     * @return
     */
    public static <T> T string2Obj(String str, Class<T> clazz) {
        if (StringUtil.StringUtilsBylang3.isEmpty(str) || clazz == null) {
            return null;
        }
        try {
            return clazz.equals(String.class) ? (T) str : objectMapper.readValue(str, clazz);
        } catch (IOException e) {
            log.error("Parse String to Object error", e);
            return null;
        }
    }

    /**
     * string转object
     *
     * @param str           json字符串
     * @param typeReference 被转对象引用类型
     * @param <T>
     * @return
     */
    public static <T> T string2ObjRef(String str, TypeReference<T> typeReference) {
        if (StringUtil.StringUtilsBylang3.isEmpty(str) || typeReference == null) {
            return null;
        }
        try {
            return (T) (typeReference.getType().equals(String.class) ? str : objectMapper.readValue(str, typeReference));
        } catch (IOException e) {
            log.error("Parse String to Object error", e);
            return null;
        }
    }

    /**
     * string转collection 用于转为集合对象
     *
     * @param str             json字符串
     * @param collectionClass 被转集合class
     * @param elementClasses  被转集合中对象类型class
     * @param <T>
     * @return
     */
    public static <T> T string2Collection(String str, Class<?> collectionClass, Class<?>... elementClasses) {
        JavaType javaType = objectMapper.getTypeFactory().constructParametricType(collectionClass, elementClasses);
        try {
            return objectMapper.readValue(str, javaType);
        } catch (IOException e) {
            throw new BadException(BadException.bulidErrorCode(ExceptionEnum.UTIL, "001"), "Parse String to Collection error", e);
        }
    }

    /*********************************************************************************************/

    /**
     * 将JSONString转换为指定对象
     *
     * @param jsonString
     * @param objectClass
     * @param <T>
     * @return
     */
    public static <T> T parseObject(String jsonString, Class<T> objectClass) {
        return JSON.parseObject(jsonString, objectClass);
    }

    /**
     * 功能描述：把JSON数据转换成指定的java对象
     *
     * @param jsonData JSON数据
     * @param clazz    指定的java对象
     * @return 指定的java对象
     */
    public static <T> T getJsonToBean(String jsonData, Class<T> clazz) {
        return JSON.parseObject(jsonData, clazz);
    }

    /**
     * 功能描述：把java对象转换成JSON数据
     *
     * @param object java对象
     * @return JSON数据
     */
    public static String getBeanToJson(Object object) {
        return JSON.toJSONString(object);
    }

    /**
     * 功能描述：把JSON数据转换成指定的java对象列表
     *
     * @param jsonData JSON数据
     * @param clazz    指定的java对象
     * @return List<T>
     */
    public static <T> List<T> getJsonToList(String jsonData, Class<T> clazz) {
        return JSON.parseArray(jsonData, clazz);
    }

    /**
     * 集合实体转换
     *
     * @param sourceList  源类型集合
     * @param targetClass 目标类型
     * @param <S>         源实体
     * @param <T>         目标实体
     * @return
     */
    public static <S, T> List<T> copyProperties(List<S> sourceList, Class<T> targetClass) {
        String jsonString = getBeanToJson(sourceList);
        return getJsonToList(jsonString, targetClass);
    }

    /**
     * 实体转换
     *
     * @param source      源类型
     * @param targetClass 目标类型
     * @param <S>         源实体
     * @param <T>         目标实体
     * @return
     */
    public static <S, T> T copyProperties(S source, Class<T> targetClass) {
        String jsonString = getBeanToJson(source);
        return parseObject(jsonString, targetClass);
    }


    /**
     * 将实体类转换为Map
     *
     * @param source
     * @param <S>
     * @return
     */
    public static <S> Map beanToMap(S source) {
        return JSON.parseObject(JSON.toJSONString(source));
    }

    /**
     * 将map转换为实体类
     *
     * @param map
     * @param targetClass
     * @param <S>
     * @return
     */
    public static <S> S mapToBean(Map map, Class<S> targetClass) {
        return JSON.parseObject(JSON.toJSONString(map), targetClass);
    }

}
